<?php
namespace yan\sign_google\factory;
use Yii;
use ExpiredException;
use phpseclib3\Crypt\AES;
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Math\BigInteger;
use yan\sign_google\jwt\Key;
class JwtVerify extends \yan\sign_google\AbstractFactory{
	public $environment;
	public $curl;
	private $token;
	private $uri;
	private $jwt;
	const grant_type='urn:ietf:params:oauth:grant-type:jwt-bearer';
	const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
    const OAUTH2_ISSUER = 'accounts.google.com';
    const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';

	function build($environment, $curl)
    {
        $this->environment = $environment;
        $this->curl = $curl;
        $this->uri = 'https://oauth2.googleapis.com/token';
        $this->jwt = \yan\sign_google\jwt\Jwt::instance();
        $this->init();
        return $this;
    }
    public function init(){

    }
	public function run($data=[]){
		$post_data = [
			'grant_type'=>self::grant_type,
			'assertion'=>$data['code'],
		];
		$idToken = $data['code'];
		$certs = $this->retrieveCertsFromLocation();
		foreach ($certs['keys'] as $cert) {
            try {
                $args = [$idToken];

                $publicKey = $this->getPublicKey($cert);                
                if (class_exists(Key::class)) {
                    $args[] = new Key($publicKey, 'RS256');
                } else {
                    $args[] = $publicKey;
                    $args[] = ['RS256'];
                }
                $payload = \call_user_func_array([$this->jwt, 'decode'], $args);

                if (property_exists($payload, 'aud')) {
                	$audience = $this->environment->getClientId();
                    if ($audience && $payload->aud != $audience) {
                        return false;
                    }
                }

                // support HTTP and HTTPS issuers
                // @see https://developers.google.com/identity/sign-in/web/backend-auth
                $issuers = [self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS];
                if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
                    return false;
                }
                
                return (array)$payload;
            } catch (ExpiredException $e) { // @phpstan-ignore-line
                return false;
            } catch (\Exception $e) {
                // continue
                return false;
            }
        }
	}
	
	public function retrieveCertsFromLocation(){
		if(($data = Yii::$app->cache->get('google_cert'))===false){
			$data = $this->curl->build(self::FEDERATED_SIGNON_CERT_URL)->run();
			Yii::$app->cache->set('google_cert', $data);
		}		
		return json_decode($data, true);
	}

	private function getPublicKey($cert)
    {
        $modulus = new BigInteger($this->jwt->urlsafeB64Decode($cert['n']), 256);
        $exponent = new BigInteger($this->jwt->urlsafeB64Decode($cert['e']), 256);
        $component = ['n' => $modulus, 'e' => $exponent];

        $loader = PublicKeyLoader::load($component);

        return $loader->toString('PKCS8');
    }
}